use crate::commands::cmd_string::{CommandGet, CommandSet};
use crate::commands::common::{ErrMsg, INVALID_ARG, UNKNOWN_CMD};
use crate::commands::parser::CommandParser;
use crate::commands::redis::{RedisRequest, RedisResponse};
use chrono::Utc;
use std::slice::Iter;

/*use phf::phf_map;
static COMMAND_TABLE: phf::Map<&'static str, Box<dyn Command>> = phf_map! {
    "GET" => Box::from(CommandGet::default()),
    "SET" => Box::from(CommandSet::default()),
};*/

pub trait Command {
    // todo: 获取所有权
    fn parse(&mut self, req: &RedisRequest) -> Result<(), ErrMsg>;

    fn execute(&self) -> Result<RedisResponse, ErrMsg>;

    fn attr(&self) -> CmdAttr;
}

pub struct CmdAttr {
    name: &'static str,

    // 命令参数个数, 正数表示需等于n, 负数表示需大于等于n
    arity: i32,
}

impl CmdAttr {
    pub(crate) const fn new(name: &'static str, arity: i32) -> Self {
        Self { name, arity }
    }

    pub(crate) fn check_arity(&self, cmd_size: i32) -> bool {
        !((self.arity > 0 && cmd_size != self.arity) || (self.arity < 0 && cmd_size < -self.arity))
    }
}

pub fn build_command(req: &RedisRequest) -> Result<Box<dyn Command>, ErrMsg> {
    let command: Option<Box<dyn Command>> = match req.get_cmd() {
        "GET" => Some(Box::new(CommandGet::default())),
        "SET" => Some(Box::new(CommandSet::default())),
        _ => None,
    };

    if command.is_none() {
        return Err(UNKNOWN_CMD);
    }

    let cmd = command.unwrap();
    if !cmd.attr().check_arity(req.get_args().len() as i32 + 2) {
        return Err(INVALID_ARG);
    }
    Ok(cmd)
}

pub fn parse_expire_flags(
    parser: &mut CommandParser<Iter<Vec<u8>>>,
    flag: &mut String,
) -> Result<Option<u64>, ErrMsg> {
    if parser.eat_eq_icase_flag("EX", flag) {
        Ok(Some((parser.take_number::<i64>()? * 1000 + Utc::now().timestamp_millis()) as u64))
    } else if parser.eat_eq_icase_flag("EXAT", flag) {
        Ok(Some((parser.take_number::<i64>()? * 1000) as u64))
    } else if parser.eat_eq_icase_flag("PX", flag) {
        Ok(Some((parser.take_number::<i64>()? + Utc::now().timestamp_millis()) as u64))
    } else if parser.eat_eq_icase_flag("PXAT", flag) {
        Ok(Some(parser.take_number::<i64>()? as u64))
    } else {
        Ok(None)
    }
}